/**********************************************************************
 * Copyright 2012 Leopold Dauvergne
 *
 * This file is part of Clock My World.
 *
 * Clock My World is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * Clock My World is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Clock My World. If not, see <http://www.gnu.org/licenses/>
 *
 ***********************************************************************/

function isLeapYear(yr)
{
    return ((yr % 4 == 0 && yr % 100 != 0) || yr % 400 == 0);
}

function Month(name, numdays, abbr)
{
    this.name = name;
    this.numdays = numdays;
    this.abbr = abbr;
}

var monthList = new Array();
monthList[0] = new Month("January", 31, "Jan");
monthList[1] = new Month("February", 28, "Feb");
monthList[2] = new Month("March", 31, "Mar");
monthList[3] = new Month("April", 30, "Apr");
monthList[4] = new Month("May", 31, "May");
monthList[5] = new Month("June", 30, "Jun");
monthList[6] = new Month("July", 31, "Jul");
monthList[7] = new Month("August", 31, "Aug");
monthList[8] = new Month("September", 30, "Sep");
monthList[9] = new Month("October", 31, "Oct");
monthList[10] = new Month("November", 30, "Nov");
monthList[11] = new Month("December", 31, "Dec");

function getJD()
{
    //console.log(parseInt(Qt.formatDateTime(datePerso.dateperso, "MM")))
    //console.log(parseInt(Qt.formatDateTime(datePerso.dateperso, "dd")))
    //console.log(parseInt(Qt.formatDateTime(datePerso.dateperso, "yyyy")))
    var docmonth = parseInt(Qt.formatDateTime(datePerso.dateperso, "M"))
    var docday =   parseInt(Qt.formatDateTime(datePerso.dateperso, "dd"))
    var docyear =  parseInt(Qt.formatDateTime(datePerso.dateperso, "yyyy"))
    if ( (isLeapYear(docyear)) && (docmonth == 2) ) {
        if (docday > 29) {
            docday = 29
        }
    } else {




        if (docday > monthList[docmonth-1].numdays) {
            docday = monthList[docmonth-1].numdays
        }
    }
    if (docmonth <= 2) {
        docyear -= 1
        docmonth += 12
    }
    var A = Math.floor(docyear/100)
    var B = 2 - A + Math.floor(A/4)
    var JD = Math.floor(365.25*(docyear + 4716)) + Math.floor(30.6001*(docmonth+1)) + docday + B - 1524.5
    return JD
}
function radToDeg(angleRad)
{
    return (180.0 * angleRad / Math.PI);
}

function degToRad(angleDeg)
{
    return (Math.PI * angleDeg / 180.0);
}

function calcTimeJulianCent(jd)
{
    var T = (jd - 2451545.0)/36525.0
    return T
}
function calcMeanObliquityOfEcliptic(t)
{
    var seconds = 21.448 - t*(46.8150 + t*(0.00059 - t*(0.001813)));
    var e0 = 23.0 + (26.0 + (seconds/60.0))/60.0;
    return e0;		// in degrees
}

function calcObliquityCorrection(t)
{
    var e0 = calcMeanObliquityOfEcliptic(t);
    var omega = 125.04 - 1934.136 * t;
    var e = e0 + 0.00256 * Math.cos(degToRad(omega));
    return e;		// in degrees
}
function calcGeomMeanLongSun(t)
{
    var L0 = 280.46646 + t * (36000.76983 + t*(0.0003032))
    while(L0 > 360.0)
    {
        L0 -= 360.0
    }
    while(L0 < 0.0)
    {
        L0 += 360.0
    }
    return L0		// in degrees
}
function calcEccentricityEarthOrbit(t)
{
    var e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
    return e;		// unitless
}
function calcGeomMeanAnomalySun(t)
{
    var M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
    return M;		// in degrees
}
function calcEquationOfTime(t)
{
    var epsilon = calcObliquityCorrection(t);
    var l0 = calcGeomMeanLongSun(t);
    var e = calcEccentricityEarthOrbit(t);
    var m = calcGeomMeanAnomalySun(t);

    var y = Math.tan(degToRad(epsilon)/2.0);
    y *= y;

    var sin2l0 = Math.sin(2.0 * degToRad(l0));
    var sinm   = Math.sin(degToRad(m));
    var cos2l0 = Math.cos(2.0 * degToRad(l0));
    var sin4l0 = Math.sin(4.0 * degToRad(l0));
    var sin2m  = Math.sin(2.0 * degToRad(m));

    var Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
    return radToDeg(Etime)*4.0;	// in minutes of time
}
function calcSunEqOfCenter(t)
{
    var m = calcGeomMeanAnomalySun(t);
    var mrad = degToRad(m);
    var sinm = Math.sin(mrad);
    var sin2m = Math.sin(mrad+mrad);
    var sin3m = Math.sin(mrad+mrad+mrad);
    var C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289;
    return C;		// in degrees
}
function calcSunTrueLong(t)
{
    var l0 = calcGeomMeanLongSun(t);
    var c = calcSunEqOfCenter(t);
    var O = l0 + c;
    return O;		// in degrees
}

function calcSunApparentLong(t)
{
    var o = calcSunTrueLong(t);
    var omega = 125.04 - 1934.136 * t;
    var lambda = o - 0.00569 - 0.00478 * Math.sin(degToRad(omega));
    return lambda;		// in degrees
}


function calcSunDeclination(t)
{
    var e = calcObliquityCorrection(t);
    var lambda = calcSunApparentLong(t);

    var sint = Math.sin(degToRad(e)) * Math.sin(degToRad(lambda));
    var theta = radToDeg(Math.asin(sint));
    return theta;		// in degrees
}
function calcHourAngleSunrise(lat, solarDec)
{
    var latRad = degToRad(lat);
    var sdRad  = degToRad(solarDec);
    var HAarg = (Math.cos(degToRad(90.833))/(Math.cos(latRad)*Math.cos(sdRad))-Math.tan(latRad) * Math.tan(sdRad));
    var HA = Math.acos(HAarg);
    return HA;		// in radians (for sunset, use -HA)
}
function calcSunriseSetUTC(rise, JD, latitude, longitude)
{
    var t = calcTimeJulianCent(JD);
    var eqTime = calcEquationOfTime(t);
    var solarDec = calcSunDeclination(t);
    var hourAngle = calcHourAngleSunrise(latitude, solarDec);
    //alert("HA = " + radToDeg(hourAngle));
    if (!rise) hourAngle = -hourAngle;
    var delta = longitude + radToDeg(hourAngle);
    var timeUTC = 720 - (4.0 * delta) - eqTime;	// in minutes
    return timeUTC
}

function isNumber(inputVal)
{
    var oneDecimal = false;
    var inputStr = "" + inputVal;
    for (var i = 0; i < inputStr.length; i++)
    {
        var oneChar = inputStr.charAt(i);
        if (i == 0 && (oneChar == "-" || oneChar == "+"))
        {
            continue;
        }
        if (oneChar == "." && !oneDecimal)
        {
            oneDecimal = true;
            continue;
        }
        if (oneChar < "0" || oneChar > "9")
        {
            return false;
        }
    }
    return true;
}


function zeroPad(n, digits) {
    n = n.toString();
    while (n.length < digits) {
        n = '0' + n;
    }
    return n;
}

function timeString(minutes, flag)
// timeString returns a zero-padded string (HH:MM:SS) given time in minutes
// flag=2 for HH:MM, 3 for HH:MM:SS
{
    if ( (minutes >= 0) && (minutes < 1440) ) {
        var floatHour = minutes / 60.0;
        var hour = Math.floor(floatHour);
        var floatMinute = 60.0 * (floatHour - Math.floor(floatHour));
        var minute = Math.floor(floatMinute);
        var floatSec = 60.0 * (floatMinute - Math.floor(floatMinute));
        var second = Math.floor(floatSec + 0.5);
        if (second > 59) {
            second = 0
            minute += 1
        }
        if ((flag == 2) && (second >= 30)) minute++;
        if (minute > 59) {
            minute = 0
            hour += 1
        }
        var output = zeroPad(hour,2) + ":" + zeroPad(minute,2);
        if (flag > 2) output = output + ":" + zeroPad(second,2);
    } else {
        var output = "error"
    }
    return output;
}
function dayString(jd, next, flag)
{
    // returns a string in the form DDMMMYYYY[ next] to display prev/next rise/set
    // flag=2 for DD MMM, 3 for DD MM YYYY, 4 for DDMMYYYY next/prev
    if ( (jd < 900000) || (jd > 2817000) ) {
        var output = "error"
    } else {
        var z = Math.floor(jd + 0.5);
        var f = (jd + 0.5) - z;
        if (z < 2299161) {
            var A = z;
        } else {
            var alpha = Math.floor((z - 1867216.25)/36524.25);
            var A = z + 1 + alpha - Math.floor(alpha/4);
        }
        var B = A + 1524;
        var C = Math.floor((B - 122.1)/365.25);
        var D = Math.floor(365.25 * C);
        var E = Math.floor((B - D)/30.6001);
        var day = B - D - Math.floor(30.6001 * E) + f;
        var month = (E < 14) ? E - 1 : E - 13;
        var year = ((month > 2) ? C - 4716 : C - 4715);
        if (flag == 2)
            var output = zeroPad(day,2) + " " + monthList[month-1].abbr;
        if (flag == 3)
            var output = zeroPad(day,2) + monthList[month-1].abbr + year.toString();
        if (flag == 4)
            var output = zeroPad(day,2) + monthList[month-1].abbr + year.toString() + ((next) ? " next" : " prev");
    }
    return output;
}
function timeDateString(JD, minutes)
{
    var output = timeString(minutes, 2) + " " + dayString(JD, 0, 2);
    return output;
}
function calcDoyFromJD(jd)
{
  var z = Math.floor(jd + 0.5);
  var f = (jd + 0.5) - z;
  if (z < 2299161) {
    var A = z;
  } else {
    var alpha = Math.floor((z - 1867216.25)/36524.25);
    var A = z + 1 + alpha - Math.floor(alpha/4);
  }
  var B = A + 1524;
  var C = Math.floor((B - 122.1)/365.25);
  var D = Math.floor(365.25 * C);
  var E = Math.floor((B - D)/30.6001);
  var day = B - D - Math.floor(30.6001 * E) + f;
  var month = (E < 14) ? E - 1 : E - 13;
  var year = (month > 2) ? C - 4716 : C - 4715;

  var k = (isLeapYear(year) ? 1 : 2);
  var doy = Math.floor((275 * month)/9) - k * Math.floor((month + 9)/12) + day -30;
  return doy;
}

function calcJDofNextPrevRiseSet(next, rise, JD, latitude, longitude, tz, dst)
{
  var julianday = JD;
  var increment = ((next) ? 1.0 : -1.0);

  var time = calcSunriseSetUTC(rise, julianday, latitude, longitude);
  while(!isNumber(time)){
    julianday += increment;
    time = calcSunriseSetUTC(rise, julianday, latitude, longitude);
  }
  var timeLocal = time + tz * 60.0 + ((dst) ? 60.0 : 0.0)
  while ((timeLocal < 0.0) || (timeLocal >= 1440.0))
  {
    var incr = ((timeLocal < 0) ? 1 : -1)
    timeLocal += (incr * 1440.0)
    julianday -= incr
  }
  return julianday;
}

function calcSunriseSet(rise,location, JD, latitude, longitude, timezone, dst)
// rise = 1 for sunrise, 0 for sunset
{
    //var id = ((rise) ? "risebox" : "setbox")
    var timeUTC = calcSunriseSetUTC(rise, JD, latitude, longitude);
    var newTimeUTC = calcSunriseSetUTC(rise, JD + timeUTC/1440.0, latitude, longitude);
    if (isNumber(newTimeUTC)) {
        var timeLocal = newTimeUTC + (timezone * 60.0)
        /*if (document.getElementById(rise ? "showsr" : "showss").checked) {
      var riseT = calcTimeJulianCent(JD + newTimeUTC/1440.0)

      showLineGeodesic(rise ? "#66ff00" : "#ff0000", riseAz)
    }*/
        timeLocal += ((dst) ? 60.0 : 0.0);
        if ( (timeLocal >= 0.0) && (timeLocal < 1440.0) ) {
            return timeString(timeLocal,2)


        } else  {
            var jday = JD
            var increment = ((timeLocal < 0) ? 1 : -1)
            while ((timeLocal < 0.0)||(timeLocal >= 1440.0)) {
                timeLocal += increment * 1440.0
                jday -= increment
            }
            return timeDateString(jday,timeLocal)


        }
    } else { // no sunrise/set found
    var doy = calcDoyFromJD(JD)
        var jdy
    if ( ((latitude > 66.4) && (doy > 79) && (doy < 267)) ||
    ((latitude < -66.4) && ((doy < 83) || (doy > 263))) )
    {   //previous sunrise/next sunset
      if (rise) { // find previous sunrise
        jdy = calcJDofNextPrevRiseSet(0, rise, JD, latitude, longitude, timezone, dst)
      } else { // find next sunset
        jdy = calcJDofNextPrevRiseSet(1, rise, JD, latitude, longitude, timezone, dst)
      }


    } else {   //previous sunset/next sunrise
      if (rise == 1) { // find previous sunrise
        jdy = calcJDofNextPrevRiseSet(1, rise, JD, latitude, longitude, timezone, dst)
      } else { // find next sunset
        jdy = calcJDofNextPrevRiseSet(0, rise, JD, latitude, longitude, timezone, dst)
      }

    }
    return dayString(jdy,0,3)

  }
}





function gettime(latitude,longitude, rise) {

    var jday = getJD()

    return calcSunriseSet(rise,"mine", jday, latitude, longitude, datePerso.timezone, 0)
    //calcSunriseSet(!rise,"mine", jday, latitude, longitude, datePerso.timezone, 0)


}

